﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;

namespace PixentrationEngine
{
    /// <summary>
    /// Utility class to manage list of high scores.
    /// </summary>
    class Scorecard
    {
        // List of scores sorted in descending
        // order from highest to lowest where
        // index 0 is highest score.
        public List<ScoreEntry> scores;

        public StreamWriter scoresWriter;
        public StreamReader scoresReader;

        public readonly DirectoryInfo DI;
        public readonly string FILE_PATH;
        public readonly string FILE_NAME;
        public readonly string TARGET;

        public const int LIST_SIZE = 10;



        /// <summary>
        /// Retrieves scores from storage.
        /// This is essentially the initialization method.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This method handles exceptions that would be 
        /// thrown if the storage location does not exist
        /// or if the saved data has been corrupted.
        /// </para>
        /// <para>
        /// Requirements:  8.3.1, 8.3.3, 8.3.4, 8.3.5, 8.3.6, 8.3.7. 
        /// </para>
        /// </remarks> 
        public void LoadScores()
        {
            scores = new List<ScoreEntry>();

            // If the file %APPDATA%\Pixentration\highscores.txt does
            // not exist, create it and populate it with dummy entries.
            if (!File.Exists(TARGET))
            {
                using (scoresWriter = new StreamWriter(File.Create(TARGET)))
                {
                    for (int i = 0; i < LIST_SIZE; i++)
                        scoresWriter.WriteLine(new ScoreEntry());
                }
            }

            // Read each line in the file %APPDATA%\Pixentration\highscores.txt.
            // Parse tokens and use the tokens to create a new ScoreEntry object
            // for each line in the file.          
            using (scoresReader = new StreamReader(TARGET))
            {
                ScoreEntry se = null;

                string entry;
                int scoresCount = 0;

                // Second test condition ensures that only the 10 highest
                // recorded scores are used.  The file would only contain
                // more than 10 scores if it had been edited manually.
                while ((entry = scoresReader.ReadLine()) != null && scoresCount < LIST_SIZE)
                {
                    try
                    {
                        string[] tokens = entry.Split(',');
                        se = new ScoreEntry(tokens[0], Convert.ToInt32(tokens[1]), tokens[2]);

                        // If converson of tokens[1] is unsuccessful, an exception
                        // will be thrown.  However, empty strings as values for
                        // tokens[0] would not be detected as problems without the
                        // following clause.
                        if (tokens[0] == "" || tokens[2] == "")
                            throw new InvalidDataException();
                    }

                    catch
                    {
                        // Replace any invalid entries
                        // with dummy entries.
                        se = new ScoreEntry();
                    }

                    finally
                    {
                        scores.Add(se);
                        scoresCount++;
                    }
                }

                // Account for any missing lines in data file.
                while (scores.Count < LIST_SIZE)
                {
                    scores.Add(new ScoreEntry());
                }
            }

            scores.Sort();

        }

        /// <summary>
        /// Save scores to storage.
        /// </summary>
        /// <remarks>
        /// Requirements:  8.3.1, 8.3.4, 8.3.6, 8.3.7.
        /// </remarks>
        public void SaveScores()
        {
          
            if (File.Exists(TARGET))
                File.Delete(TARGET);

            using (scoresWriter = new StreamWriter(File.Create(TARGET)))
            {
                foreach (ScoreEntry s in scores)
                    scoresWriter.WriteLine(s.Name + "," + s.Score + "," + s.Time);
            }
        }

        /// <summary>
        /// Retrieves saved scores at client's request.
        /// </summary>
        /// <returns>List of high scores, sorted in descending order.</returns>
        /// <remarks>
        /// Requirements:  8.3.1, 8.3.3, 8.3.5.
        /// </remarks>
        public List<ScoreEntry> GetScores()
        {
            scores.Sort();
            return scores;
        }


        /// <summary>
        /// Compares a score with the lowest saved high score.
        /// </summary>
        /// <param name="entry">
        /// ScoreEntry object to be compared to the lowest
        /// saved high score.
        /// </param>
        /// <returns>
        /// <para>
        /// True if the value of the argument's score
        /// attribute is greater than the value of the
        /// lowest saved high score.
        /// </para>
        /// <para>
        /// False if the value of the argument's score
        /// attribute is less than the value of the
        /// lowest saved high score.
        /// </para>
        /// </returns>
        /// <remarks>
        /// Requirements:  8.3.1, 8.3.2, 8.3.5.
        /// </remarks>
        public bool isHighScore(ScoreEntry entry)
        {
            scores.Sort();

            // Get the lowest saved high score.
            ScoreEntry lastEntry = scores[scores.Count - 1];

            // Compare score from current game to 
            // lowest saved high score.
            return (entry.Score > lastEntry.Score);
        }

        
        
        /// <summary>
        /// <para>
        /// Adds a new ScoreEntry object to the saved list of
        /// high scores if the value of its score attribute
        /// is higher than the lowest saved score.
        /// </para>
        /// <para>
        /// If a new object is added to the list of saved scores, this method
        /// removes the ScoreEntry object with the lowest score
        /// from the list and then sorts the list.
        /// </para>
        /// </summary>
        /// <param name="newEntry">Score to add or reject.</param>
        /// <returns>
        /// True if newEntry has been added to the list of high
        /// scores; false if newEntry has not been added.
        /// </returns>
        /// <remarks>
        /// Requirements:  8.3.1, 8.3.2, 8.3.5.
        /// </remarks>
        public bool NewScore(ScoreEntry newEntry)
        {
            if (isHighScore(newEntry))
            {
                // Ensure that the lowest saved high score is the
                // last entry in the list.
                scores.Sort();

                // Replace the lowest saved high score with the
                // score from the current game.
                ScoreEntry lastEntry = scores[scores.Count - 1];
                scores.Remove(lastEntry);
                scores.Add(newEntry);

                // Sort the list again so that the new high score
                // is placed in the proper position in the list.
                scores.Sort();

                return true;  // Confirm that new high score was added.
            }

            return false;  // Score from current game is not a new high score.
        }


        /// <summary>
        /// Default constructor for the Scorecard class.  Initializes
        /// read-only variables that are used to access the storage
        /// location for scores.
        /// </summary>
        /// <remarks>
        /// Requirements:  8.3.7.
        /// </remarks>
        public Scorecard()
        {
            // Using this constructor to initialize read-only variables
            // was the only way I could get the path and create
            // the directory.

            FILE_PATH = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)
                + "\\Pixentration";

            DI = Directory.CreateDirectory(FILE_PATH);

            FILE_NAME = "highscores.txt";

            TARGET = FILE_PATH + "\\" + FILE_NAME;
        }

    }

}
